//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#include <buola/buola.h>
#include <buola/app/ccmdline.h>
#include <buola/io.h>

namespace buola {
    
namespace detail {

std::vector<CCmdLineFlag*> &all_cmd_line_options()
{
    static std::vector<CCmdLineFlag*> lV;
    return lV;
}
    
/*namespace detail*/ }

CCmdLine &cmd_line()
{
    static CCmdLine lL;
    return lL;
}

void CCmdLine::DisplayHelp()
{
    msg_info() << "\e[1mUsage: \e[0m" << cmd_line().GetCommand() << " [OPTIONS]\n\n";
    msg_info() << "\e[1mAllowed options\e[0m\n";
    for(int k=0;k<detail::all_cmd_line_options().size();k++)
    {
        CCmdLineFlag *lThis=detail::all_cmd_line_options()[k];

        msg_info() << "  ";
        if(lThis->mShort)
        {
            msg_info() << '-' << lThis->mShort << ", ";
        }
        else
        {
            msg_info() << "    ";
        }
        if(lThis->mLong.length())
        {
            msg_info() << "--" << lThis->mLong;
        }
        msg_info() << "\e[25G" << lThis->mHelp << "\n";
    }
}

void CCmdLine::Parse(int pNArg,char **pArgs)
{
    if(mParsed) throw XAlready("trying to parse already parsed CCmdLine");

    mCommand=pNArg?pArgs[0]:"(unknown)";
    
    std::vector<std::string> lArgs;
    
    for(int i=0;i<pNArg;i++)
        lArgs.emplace_back(pArgs[i]);

    bool lMarkFound=false;

    std::vector<CCmdLineFlag*> &lOptions=detail::all_cmd_line_options();
    
    for(int i=1;i<lArgs.size();i++)
    {
        if(lArgs[i][0]!='-'||lMarkFound||lArgs[i].length()==1) // parse '-' as an argument
        {
            mArgs.push_back(lArgs[i]);
            continue;
        }

        if(lArgs[i][1]=='-') //long option
        {
            if(lArgs[i].length()==2) // '--' termination mark
            {
                lMarkFound=true;
                continue;
            }

            int lEqual=lArgs[i].find('=',2);

            CCmdLineFlag *lOption=nullptr;

            std::string lName=(lEqual>2)?lArgs[i].substr(2,lEqual-2)
                                    :lArgs[i].substr(2);

            for(CCmdLineFlag *o : lOptions)
            {
                if(lName==o->mLong)
                {
                    lOption=o;
                    break;
                }
            }

            if(!lOption)
            {
                throw XOptionParsing(I18N("unknown command line option: ")+lArgs[i]);
            }

            void *lData=nullptr;

            if(lEqual>=2)
            {
                lData=lOption->ParseParameter(lArgs[i].substr(lEqual+1));
            }
            else if(lOption->HasParameter())
            {
                if((++i)>=lArgs.size())
                {
                    throw XOptionParsing(I18N("option needed parameter but it is not present"));
                }
                lData=lOption->ParseParameter(lArgs[i]);
            }
            
            mOptions[lOption]=lData;
        }
        else    //short option
        {
            for(int j=1;j<lArgs[i].length();j++)
            {
                CCmdLineFlag *lOption=nullptr;

                for(CCmdLineFlag *o : lOptions)
                {
                    if(lArgs[i][j]==o->mShort)
                    {
                        lOption=o;
                        break;
                    }
                }

                if(!lOption)
                {
                    throw XOptionParsing(I18N("unknown command line option: -")+lArgs[i][j]);
                }

                void *lData=nullptr;

                if(lOption->HasParameter())
                {
                    if(++j<lArgs[i].length())
                    {
                        if(lArgs[i][j]=='=')
                        {
                            lData=lOption->ParseParameter(lArgs[i].substr(j+1));
                        }
                        else
                        {
                            throw XOptionParsing("option needed parameter but it is not present");
                        }
                    }
                    else
                    {
                        if((++i)>=lArgs.size())
                        {
                            throw XOptionParsing("Option needed parameter but it is not present");
                        }
                        lData=lOption->ParseParameter(lArgs[i]);
                    }
                }
                mOptions[lOption]=lData;
                if(lData) break;
            }
        }
    }

    for(CCmdLineFlag *o : lOptions)
    {
        if(!IsSet(*o)&&o->Required())
            throw XOptionParsing("required option not set");
    }
    
    mParsed=true;
}

/*namespace buola*/ }
